/*
 * Licensed to Nanjing RedOrange Co. ltd (RedOrange) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * RedOrange licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package redora.rdo.impl;

import java.io.File;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.mysql.jdbc.JDBC4Connection;

import redora.rdo.configuration.Configuration;
import redora.rdo.configuration.DatabaseType;
import redora.rdo.exceptions.ConnectException;

/**
 * Central connection manager that connects to the database as set up in
 * /etc/redorange/configuration.xml.
 * This factory primarily loads and caches the configuration file.
 * 
 * @author jaap
 */
public class DatabaseFactory {
	private static Logger l = Logger.getLogger("redora.rdo.impl.Database");

	/** Database configuration file etc/redora/configuration.xml */
	public static final String CONFIGURATION_FILE = File.separator + "etc"
			+ File.separator + "redora" + File.separator
			+ "configuration.xml";

	private static DatabaseType config;
	
	private DatabaseFactory(){		
		
	}

	/** Loads and caches the configuration file */
	public static DatabaseType configuration() {
		if (config == null) {
			l.log(Level.FINE, "Loading configuration");
			try {
				JAXBContext jc = JAXBContext
						.newInstance("redora.rdo.configuration");
				Unmarshaller u = jc.createUnmarshaller();
				Configuration conf = (Configuration) u.unmarshal(new File(
						CONFIGURATION_FILE));
				config = conf.getDatabase();
			} catch (JAXBException e) {
				l
						.log(
								Level.SEVERE,
								"Cannot open the configuration file, or the configuratiopn file is not valid",
								e);
			}
		}
		return config;
	}

	/** Get database connections */
	public static JDBC4Connection connect() throws ConnectException {
		JDBC4Connection retval = null;
		try {
			if (configuration().getJdbcConnection() != null) {
				l.log(Level.FINE, "JDBC connect with: "
						+ configuration().getJdbcConnection().getDatabase());
				retval = (JDBC4Connection) DriverManager.getConnection(
						"jdbc:mysql://"
								+ configuration().getJdbcConnection().getHost()
								+ "/"
								+ configuration().getJdbcConnection()
										.getDatabase(), configuration()
								.getJdbcConnection().getUsername(),
						configuration().getJdbcConnection().getPassword());
			} else {
				l.log(Level.FINE, "Datasource connection: "
						+ configuration().getDatasourceConnection());
				Context ctx = new InitialContext();
				DataSource ds = (DataSource) ctx.lookup(configuration()
						.getDatasourceConnection());
				retval = (JDBC4Connection) ds.getConnection();
			}

			l.fine("Connected");
		} catch (SQLException e) {
			l.log(Level.SEVERE, "Failed to connect to database "
					+ configuration().getJdbcConnection().getUsername() + "@"
					+ configuration().getJdbcConnection().getHost() + "/"
					+ configuration().getJdbcConnection().getDatabase(), e);
			throw new ConnectException("Failed to connect to database "
					+ configuration().getJdbcConnection().getUsername() + "@"
					+ configuration().getJdbcConnection().getHost() + "/"
					+ configuration().getJdbcConnection().getDatabase(), e);
		} catch (NamingException e) {
			l.log(Level.SEVERE, "Cannot find or open datasource "
					+ configuration().getDatasourceConnection(), e);
			throw new ConnectException("Cannot find or open datasource "
					+ configuration().getDatasourceConnection(), e);
		}

		return retval;
	}
}
